#include <trap.h>
#include <um.h>
#include <proc.h>
#include <stdio.h>
#include <stub.h>
#include <memlayout.h>
#include <mmu.h>
#include <pmm.h>
#include <vmm.h>

/*
 * Signals that are OK to receive in the stub - we'll just continue it.
 * SIGWINCH will happen when UML is inside a detached screen.
 */
#define STUB_SIG_MASK ((1 << SIGVTALRM) | (1 << SIGWINCH))

/* Signals that the stub will finish with - anything else is an error */
#define STUB_DONE_MASK (1 << SIGTRAP)

int
wait_stub_done(int pid)
{
	int n, status, err;

	struct user_regs_struct regs;

	while (1) {
		n = syscall3 (__NR_waitpid, pid, (long)&status, WUNTRACED | __WALL);
		/* err = syscall4 (__NR_ptrace, PTRACE_GETREGS, pid, 0, (long)&regs); */
		/* cprintf ("stub: is exited: %d\n", WIFEXITED (status)); */
		/* cprintf ("stub: is stopped: %d\n", WIFSTOPPED (status)); */
		/* cprintf ("stub: is signaled: %d\n", WIFSIGNALED (status)); */
		/* cprintf ("stub: is continued: %d\n", WIFCONTINUED (status)); */
		/* cprintf ("stub: term sig: %d\n", WTERMSIG (status)); */
		if (WIFEXITED (status)) {
			cprintf ("stub: status = 0x%x\n", status);
			cprintf ("stub: err = %d\n", err);
			cprintf ("stub: eip = 0x%x\n", regs.eip);
		}
		
		if ((n < 0) || !WIFSTOPPED(status))
			goto bad_wait;

		if (((1 << WSTOPSIG(status)) & STUB_SIG_MASK) == 0)
			break;

		err = syscall4 (__NR_ptrace, PTRACE_CONT, pid, 0, 0);
		if (err) {
			return -2;
		}
	}

	if (((1 << WSTOPSIG(status)) & STUB_DONE_MASK) != 0)
		return 0;

	return -3;

bad_wait:
	return -1;
}

int
get_faultinfo (int pid, struct faultinfo* fi)
{
	int err;

	siginfo_t siginfo;
	err = syscall4 (__NR_ptrace, PTRACE_GETSIGINFO, pid, 0, (long)&siginfo);
	if (err != 0)
		return -4;
	
	fi->cr2 = (int)siginfo.si_addr;
	fi->trap_no = siginfo.si_trapno;
	pte_t* pte;
	switch (siginfo.si_code) {
	case SEGV_MAPERR:
		fi->error_code = 4;
		break;
	case SEGV_ACCERR:
		pte = get_pte (current->mm->pgdir, fi->cr2, 0);
		//cprintf ("SEGV_ACCERR: 0x%x\n", *pte);
		if (*pte & PTE_A)
			fi->error_code = 7;
		else
			fi->error_code = 5;
		break;
	}
	
	/* err = syscall4 (__NR_ptrace, PTRACE_CONT, pid, 0, SIGSEGV); */
	/* if (err != 0) */
	/* 	return -1; */

	/* err = wait_stub_done (pid); */
	/* if (err < 0) */
	/* 	return err - 10; */

	/* memcpy(fi, current->arch.stub_stack, sizeof(*fi)); */

	return 0;
}

int
nullify_syscall (int pid, struct um_pt_regs *regs) 
{
	int err, status;
	
	err = syscall4 (__NR_ptrace, PTRACE_POKEUSR, pid, 44, __NR_getpid);
	if (err < 0) {
		cprintf ("nullify_syscall: cannot nullify\n");
		return -1;
	}

	err = syscall4 (__NR_ptrace, PTRACE_SYSCALL, pid, 0, 0);
	if (err < 0) {
		cprintf ("nullify_syscall: cannot continue syscall\n");
		return -1;
	}

	err = syscall3 (__NR_waitpid, pid, (long)&status, WUNTRACED | __WALL);
	if (err < 0 || !WIFSTOPPED (status) || (WSTOPSIG(status) != (SIGTRAP | 0x80))) {
		cprintf ("nullify_syscall: wait error\n");
		cprintf ("\terr = %d\n", err);
		cprintf ("\tis stopped = %d\n", WIFSTOPPED (status));
		cprintf ("\tstop sig = %d\n", WSTOPSIG(status));
		return -1;
	}

	/* struct user_regs_struct uregs; */
	/* err = syscall4 (__NR_ptrace, PTRACE_GETREGS, pid, 0, (long)&uregs); */
	/* if (err < 0) */
	/* 	return -1; */
	/* cprintf ("return eax = %d\n", uregs.eax); */

	return 0;
}
